const unzipper = require('unzipper');
const fs = require('fs');
const path = require('path');
const unzipCMD = require('./unzipCMD');

// const BrotliDecompress = require('minizlib');
// 密码生成配置相关
const nums = [1, 6]; // 密码可能位数 1~6
// const last_record = null;// 上次运行记录 null || {t: ,pwd:'xxx'}
const last_record = { t: 1, pwd: '1000', };// 上次运行记录 null || {t: ,pwd:'xxx'}

const bfl = 150; // 并发量 

function main() {
    // testUnzipper();
    // testUnzipCMD();
    /**
     * bfl  100
     * time 12.306
     */
    testBingfa();
    // DVWA();
}

function zipFilesOps() {
    // 读取压缩包文件

    // const input = sourceOfCompressedData()
    // const decode = new BrotliDecompress()
    // const output = whereToWriteTheDecodedData()
    // input.pipe(decode).pipe(output)


}

function testUnzipper() {
    let fun = async function () {
        // const directory = await unzipper.Open.file('./source/test1.zip');
        // const directory = await unzipper.Open.file('./source/test1-0-123a.zip'); // zip 经典加密方式
        const directory = await unzipper.Open.file('./source/test1-123a.zip');// zip 非经典加密方式 ！！！ 无法解压 非经典加密方式
        console.log('directory', directory);
        let pwd = `123a`;
        return new Promise((resolve, reject) => {
            directory.files[0]
                .stream(pwd)
                .pipe(fs.createWriteStream('firstFile'))
                .on('error', reject)
                .on('finish', resolve)
        });
    }

    fun().then(res => {

    }).catch(err => {

    });
}
// 并发测试
async function testBingfa() {
    let finished = false;

    var outPath = './file';//文件夹路径是否需要创建
    if (!fs.existsSync(outPath)) {
        fs.mkdirSync(outPath);
    }

    let cmdPath = "D:/Program Files (x86)/WinRAR"; // D:\Program Files (x86)\WinRAR

    // let zipPath = './source/test1-0-123a.zip'; // zip经典加密
    /**@todo 文件名获取，解压到file/文件名 */
    let zipPath = './source/test1-123a.zip';// zip非经典加密
    // let zipPath = './source/xxx.zip';// 

    zipPath = path.resolve(zipPath);
    outPath = path.resolve(outPath);


    let t = 1;
    let val = "0";
    if (!!last_record) {
        t = last_record.t;
        val = last_record.pwd;
    }
    let pwd = new CreatePwd({
        val,
        nums,
    });
    console.time("xx");
    while (!finished) {
        // console.log("times: ", t, " pwd: ", pwd.val); // 记录当前处理到的密码，方便中断重启
        console.log("并发批次: ", t, "开始");
        let arr = [];
        let data = [];
        for (let i = 0; i < bfl; i++) {
            data.push({
                pwd: pwd.val,
            });
            arr.push(
                unzipCMD.unzipCmd(cmdPath, zipPath, outPath, pwd.val)
            );

            let res = pwd.next();
            if (res == null) {
                console.log("密码穷解完了");
                break;
                finished = true;
            }
        }

        await Promise.allSettled(arr).then(res => {
            for (let index = 0; index < res.length; index++) {
                let el = res[index];
                let pwd = data[index].pwd;
                if (el.status === "fulfilled") {
                    let rv = el.value;
                    let code = rv;
                    if (code === 0) {

                        console.log("找到密码，密码为: ", pwd);
                        finished = true;
                        break;
                    } else {
                        // console.log("密码错误");

                    }
                } else if (el.status === "rejected") {
                    let rv = el.reason;
                    console.log("程序出错 pwd: ", pwd);
                }
            };
        });

        console.log("并发批次: ", t, `结束 pwd ${data[0].pwd} ~ ${data[bfl - 1].pwd}`);
        /**
        // 测试pwd.next
        let res = pwd.next();
        if (res == null) {
            console.log("密码穷解完了");
            finished = true;
        }
        */



        // .catch((res) => {
        //     console.log("出错啦");
        //     finished = true;
        // });



        t++;
        // if (t > 99) {
        //     console.log("？？？？");
        //     // 防死循环，temp
        //     finished = true;
        // }
    }
    console.timeEnd("xx");
}

async function DVWA() {
    let finished = false;

    var outPath = './file';//文件夹路径是否需要创建
    if (!fs.existsSync(outPath)) {
        fs.mkdirSync(outPath);
    }

    let cmdPath = "D:/Program Files (x86)/WinRAR"; // D:\Program Files (x86)\WinRAR

    // let zipPath = './source/test1-0-123a.zip'; // zip经典加密
    let zipPath = './source/test1-123a.zip';// zip非经典加密
    // let zipPath = './source/xxx.zip';// 

    zipPath = path.resolve(zipPath);
    outPath = path.resolve(outPath);


    let t = 1;
    let val = "0";
    if (!!last_record) {
        t = last_record.t;
        val = last_record.pwd;
    }
    let pwd = new CreatePwd({
        val,
        nums,
    });
    console.time("xx");
    while (!finished) {
        if(t % bfl == 0){
            // 减少log打印
            console.log("times: ", t, " pwd: ", pwd.val); // 记录当前处理到的密码，方便中断重启
        }
        

        /**
        // 测试pwd.next
        let res = pwd.next();
        if (res == null) {
            console.log("密码穷解完了");
            finished = true;
        }
        */

        await unzipCMD.unzipCmd(cmdPath, zipPath, outPath, pwd.val)
            .then(function (code) {
                if (code === 0) {
                    console.log("找到密码，密码为: ", pwd.val);
                    finished = true;
                } else {
                    // console.log("密码错误");
                    let res = pwd.next();
                    if (res == null) {
                        console.log("密码穷解完了");
                        finished = true;
                    }
                }
            });

        // .catch((res) => {
        //     console.log("出错啦");
        //     finished = true;
        // });



        t++;
        // if (t > 99) {
        //     console.log("？？？？");
        //     // 防死循环，temp
        //     finished = true;
        // }
    }
    console.timeEnd("xx");// 对比发现并发效率更高
}

class CreatePwd {
    val = null;// 密码值；格式为字符串
    n = 0;// 当前密码位数
    pos = -1;// 操作序位，从左到右，从0 ~ ?

    nums = [1, 6];// 密码位数

    CHARS = [];

    constructor({
        /** 上次值，不填默认值为"0" */
        val = "0",
        /** 密码位数限定 */
        nums = [1, 6] } = {
            val: "0",
            nums: [1, 6],
        }) {
        this.val = val;// 初始值 | 当前初值
        this.n = this.val.toString().length;// 当前密码位数
        this.pos = this.n - 1;// 操作位置于末位，类似于个位
        this.nums[0] = nums[0];
        this.nums[1] = nums[1];

        for (let i = 0; i <= 9; i++) {
            this.CHARS.push(i + '');
        }

        // 0~9 48~57
        // A~Z 65~90
        // a~z 97~122
        for (let i = 97; i <= 122; i++) {
            this.CHARS.push(String.fromCharCode(i));
        }

        for (let i = 65; i <= 90; i++) {
            this.CHARS.push(String.fromCharCode(i));
        }
        // console.log(this.CHARS);
    }
    /**
     * 递进规则 
     *      数字，小写英文字母，大写英文字母  特殊字符 （暂不考虑）
     *      各个位数递进顺序，从前到后
     * 0->9
     * a->z
     * z->A
     * A->Z
     * 
     * 返回: 下一个密码 | null（穷尽 无下一个） 
     */
    next() {
        if (!this.isOver()) {
            this.val = this._bit_add(this.val, this.pos);

            return this;

        } else {
            if (this.n < this.nums[1]) {
                // 位数变更
                this.n += 1;
                this.pos = this.n - 1; // 操作位大部分时候都为末位，除进位时候
                this.val = Array(this.n).fill('0').join("");
                return this;

            } else if (this.n === this.nums[1]) {
                return null;
            }
        }
    }
    // 当前位数密码是否穷举完
    isOver() {
        let last = Array(this.n).fill(this.CHARS[this.CHARS.length - 1]).join("");
        return last === this.val;
    }

    /**
     * 指定字符集（this.CHARS）指定位自增,进位
     * @param {string} str 数用字符表示
     * @param {number} index 当前操作哪个位
     * @param {number} [step=1] 可选参数，默认+1
     */
    _bit_add(str, index, step = 1) {
        let arr = str.split('');
        let x = arr[index];
        if (x !== this.CHARS[this.CHARS.length - 1]) {
            let idx = this.CHARS.findIndex(v => v === x);
            x = this.CHARS[idx + step];
            arr.splice(index, 1, x);
            return arr.join("");
        } else {
            arr.splice(index, 1, 0);// 当前位（末位）补零
            return this._bit_add(arr.join(""), index - 1);// 前一个数位进一
        }
    }
}




/**
 * 原文链接：https://blog.csdn.net/wanglei19880622/article/details/114387077
 */
function testUnzipCMD() {

    var outPath = './file';//文件夹路径是否需要创建
    if (!fs.existsSync(outPath)) {
        fs.mkdirSync(outPath);
    }

    let cmdPath = "D:/Program Files (x86)/WinRAR"; // D:\Program Files (x86)\WinRAR

    // let cmdPath = "D://Program Files (x86)//360压缩//360//360zip";// D:\Program Files (x86)\360压缩\360\360zip
    // let zipPath = './source/test1-0-123a.zip';
    let zipPath = './source/test1-123a.zip';
    let pwd = "123a";

    zipPath = path.resolve(zipPath);
    outPath = path.resolve(outPath);


    unzipCMD.unzipCmd(cmdPath, zipPath, outPath, pwd)
        .then(function (res) {
            console.log(res);
        });
}

main();
